import Dexie from 'dexie'

// 通过拦截post请求,请求成功则返回原数据,失败则数据库查询;
export const addPostEventListener = (version) => {
  addEventListener('fetch', function(event) {
    if (event.request.method === 'POST') {
      let db = new Dexie('post_cache')
      db.version(version).stores({
        post_cache: 'key,response,timestamp'
      })
      event.respondWith(
        fetch(event.request.clone())
          .then(function(response) {
            cachePut(event.request.clone(), response.clone(), db.post_cache)
            return response
          })
          .catch(function(e) {
            console.log('sw:fetch error', e)
            // 未命中缓存返回503
            return cacheMatch(event.request.clone(), db.post_cache)
          })
      )
    }
  })
}

/**
 * Serializes a Request into a plain JS object.
 *
 * @param request
 * @returns Promise
 */
function serializeRequest(request) {
  var serialized = {
    url: request.url,
    headers: serializeHeaders(request.headers),
    method: request.method,
    mode: request.mode,
    credentials: request.credentials,
    cache: request.cache,
    redirect: request.redirect,
    referrer: request.referrer
  }

  if (request.method !== 'GET' && request.method !== 'HEAD') {
    return request.clone().text().then(function(body) {
      serialized.body = body
      return Promise.resolve(serialized)
    }).catch(e => {
      console.log(e)
    })
  }
  return Promise.resolve(serialized)
}

/**
 * Serializes a Response into a plain JS object
 *
 * @param response
 * @returns Promise
 */
function serializeResponse(response) {
  var serialized = {
    headers: serializeHeaders(response.headers),
    status: response.status,
    statusText: response.statusText
  }

  return response.clone().text().then(function(body) {
    serialized.body = body
    return Promise.resolve(serialized)
  })
}

/**
 * Serializes headers into a plain JS object
 *
 * @param headers
 * @returns object
 */
function serializeHeaders(headers) {
  var serialized = {}
// `for(... of ...)` is ES6 notation but current browsers supporting SW, support this
// notation as well and this is the only way of retrieving all the headers.
  for (var entry of headers.entries()) {
    serialized[entry[0]] = entry[1]
  }
  return serialized
}

/**
 * Creates a Response from it's serialized version
 *
 * @param data
 * @returns Promise
 */
function deserializeResponse(data) {
  return Promise.resolve(new Response(data.body, data))
}

/**
 * Saves the response for the given request eventually overriding the previous version
 *
 * @param data
 * @returns Promise
 */
function cachePut(request, response, store) {
  var key, data
  getPostId(request.clone())
    .then(function(id) {
      key = JSON.stringify(id)
      return serializeResponse(response.clone())
    })
    .then(function(serializedResponse) {
      data = serializedResponse
      var entry = {
        key: key,
        response: data,
        timestamp: Date.now()
      }
      store
        .add(entry)
        .catch(function(error) {
          store.update(entry.key, entry)
        })
    })
    .catch(e => {
      console.log(e)
    })
}

/**
 * Returns the cached response for the given request or an empty 503-response for a cache miss.
 *
 * @param request
 * @param store
 * @return Promise
 */
function cacheMatch(request,store) {
  return getPostId(request.clone())
    .then(function(id) {
      return store.get(JSON.stringify(id))
    }).then(function(data) {
      if (data) {
        return deserializeResponse(data.response)
      } else {
        return new Response('', { status: 503, statusText: 'Service Unavailable' })
      }
    })
}

/**
 * Returns a string identifier for our POST request.
 *
 * @param request
 * @return Promise
 */

function getPostId(request) {
  return serializeRequest(request.clone())
}
